Persistent Storage
Persistent storage is a way to retain data on a long-term basis, supported by a nonvolatile device such as a hard disk. Persistent data remains stable between computing sessions. All persistent storage in OpenDoc is represented by storage units (ODStorageUnit), which provide a standard, cross-platform interface for all persistent objects. Every object in OpenDoc that needs to maintain its state between sessions is a persistent object, and each has a storage unit. Part objects must handle their storage units in a particularly disciplined manner because they need to satisfy many more requirements than other persistent objects.Storage units have any number of properties, which are like separate forks of files, and properties have any number of values, which are separate streams of each fork. Each value in the same property holds a different representation of the same data; it should not hold different data. For example, every part has a contents property (
kODPropContents), and multiple representations of the content can be stored in different values, but only content data should be stored in the contents property.If parts have multiple representations of their content, they must write them to storage in order of fidelity. For example, a part's most faithful representation of text may be styled text, while a lower fidelity representation of the same content would be plain ASCII text, a separate value for which would be added later to the same property. The highest fidelity representation of part content is its native format, specific to and usually proprietary to the part editor that created it. Lower fidelity representations enable the part to be viewed in documents without a full complement of part editors, to maintain portability of documents.
To load your part's content into memory from persistent storage, you should basically reverse the process of writing your part. However, your part must be able to work from an empty storage unit as well as one with stored content. Refer to the section "Initialization" for a description of this process.
SamplePart implements its persistent storage protocol in its
Externalize,ExternalizeStateInfo, andExternalizeContentmethods. Other methods dealing with storage areWritePartInfo,ReadPartInfo,ClonePartInfo,CloneInto, andPurge. In addition, the utility methodSetDirtymanipulates the dirty flag, which is simply a Boolean value SamplePart uses to avoid redundancy: it writes the part content and notifies the draft only if the part has been altered.The sections that follow show the implementations of these methods, except for the
CloneIntomethod, which OpenDoc calls to perform data interchange. TheCloneIntomethod also uses the storage unit API.The Externalize Method
OpenDoc calls theExternalizemethod whenever it is necessary to write the part to persistent storage. Your part can also call its ownExternalizemethod whenever it wants to. Before returning from this method, you must write all data that you need to accurately re-create the content and state of your part.This method must call its parent class behavior (inherited class), because one of its parent class methods contains implementation. This is done in the SOM class implementation, which otherwise delegates all implementation to this method. Refer to the
som_SamplePart__Externalizemethod of thesom_SamplePartclass in the file som_SamplePart.cpp.The
SamplePartobject's implementation of theExternalizemethod performs the following actions:
Listing 2-40 shows the implementation of the
- Checks the part's dirty flag and storage unit privileges.
If the part's dirty flag is set tokODTrue, meaning that the part has been changed since it was last written, and if the part's storage unit is not read-only, the method proceeds.
- Retrieves a pointer to the part's storage unit.
The method calls theGetStorageUnitmethod inherited from theODPartsuperclassODPersistentObject, using thefSelffield to refer to the part editor.
- Ensures that the storage unit properties are appropriate.
The method calls SamplePart subroutines, internal methodsCheckAndAddPropertiesandCleanseContentProperty, to verify that the properties and values are correct.
- Writes out the part's status information.
The method accomplishes this step by calling an internal method,ExternalizeStateInfo.
- Writes out the part's content data.
The method writes out its content data by calling another internal method,ExternalizeContent.
- Sets the part's dirty flag to false.
Externalizemethod. The other methods that theExternalizemethod calls are described in the next few sections.Listing 2-40
Externalizemethod
void SamplePart::Externalize( Environment* ev ) { SOM_Trace("SamplePart","Externalize"); TRY if ( fDirty && !fReadOnlyStorage ) { ODStorageUnit* storageUnit = fSelf->GetStorageUnit(ev); this->CheckAndAddProperties(ev, storageUnit); this->CleanseContentProperty(ev, storageUnit); this->ExternalizeStateInfo(ev, storageUnit, kODNULLKey, kODNULL); this->ExternalizeContent(ev, storageUnit, kODNULLKey, kODNULL); fDirty = kODFalse; } CATCH_ALL this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrExternalizeFailed); SetErrorCode(kODErrAlreadyNotified); RERAISE; ENDTRY }The CheckAndAddProperties Method
SamplePart calls its own internalCheckAndAddPropertiesmethod to verify that the part's storage unit has the properties it needs to run. If such properties are not present,CheckAndAddPropertiesadds them.The
CheckAndAddPropertiesmethod performs the following actions:
Listing 2-41 shows the implementation of the
- Sets up the contents property if it is not present.
After ensuring that the contents property exists, the method checks for, and if necessary adds, the part's kind value to the contents property. These actions are necessary in case the storage unit is new and the part has not been previously written to storage.
- Sets up the preferred kind property if it is not present.
The method writes out the default part kind for the editor. The user's chosen kind is written out in theExternalizeStateInfomethod.
- Sets up the part's display frame list if it is not present.
The method checks for, and if necessary adds, the display frames property and value.
CheckAndAddPropertiesmethod.Listing 2-41
CheckAndAddPropertiesmethod
void SamplePart::CheckAndAddProperties( Environment* ev, ODStorageUnit* storageUnit ) { SOM_Trace("SamplePart","CheckAndAddProperties"); if ( !storageUnit->Exists(ev, kODPropContents, kODNULL, 0) ) storageUnit->AddProperty(ev, kODPropContents); if ( !storageUnit->Exists(ev, kODPropContents, kSamplePartKind, 0) ) { storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 0, kODPosAll); storageUnit->AddValue(ev, kSamplePartKind); } if ( !storageUnit->Exists(ev, kODPropPreferredKind, kODISOStr, 0) ) { TRY ODSetISOStrProp(ev, storageUnit, kODPropPreferredKind, kODISOStr, kSamplePartKind); CATCH_ALL ODSURemoveProperty(ev, storageUnit, kODPropPreferredKind); ENDTRY } if ( !storageUnit->Exists(ev, kODPropDisplayFrames, kODNULL, 0) ) storageUnit->AddProperty(ev, kODPropDisplayFrames); if ( !storageUnit->Exists(ev, kODPropDisplayFrames, kODWeakStorageUnitRefs, 0) ) { storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined, kODNULL, 0, kODPosAll); storageUnit->AddValue(ev, kODWeakStorageUnitRefs); } }The CleanseContentProperty Method
TheSamplePartobject calls its own internalCleanseContentPropertymethod from itsExternalizemethod. The purpose of this method is to remove any value in the contents property that the part cannot write out accurately, such as values added to the contents property during drag-and-drop operations.The
CleanseContentPropertymethod performs the following actions:
Listing 2-42 shows the implementation of the
- Focuses the storage unit to its contents property.
- Retrieves the type of each value in the contents property.
The method uses the count of the number of values in the contents property to iterate through all of them. It focuses the storage unit on each value and gets its type.
- Removes any unsupported values.
The method uses the OpenDoc utility methodODISOStrCompareto identify unsupported values by comparing their types to thekSamplePartKinddata type. The method then deletes unsupported values using theODStorageUnitmethodRemoveon the previously focused storage unit.
CleanseContentPropertymethod.Listing 2-42
CleanseContentPropertymethod
void SamplePart::CleanseContentProperty( Environment* ev, ODStorageUnit* storageUnit ) { SOM_Trace("SamplePart","CleanseContentProperty"); ODULong numValues; ODULong index; storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 0, kODPosAll); numValues = storageUnit->CountValues(ev); for (index = numValues; index >= 1; index--) { storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, index, kODPosUndefined); TempODValueType value = storageUnit->GetType(ev); if ( ODISOStrCompare(value, kSamplePartKind) != 0 ) storageUnit->Remove(ev); } }The ExternalizeStateInfo Method
TheSamplePartobject calls its internalExternalizeStateInfomethod from itsExternalizemethod when it writes the part to storage. This method writes out state information--any information pertaining to the working of the part editor--rather than the content. Such state information may be lost during data interchange operations, so the part must be able to recover gracefully if the state information is incomplete or missing.The
ExternalizeStateInfomethod performs the following actions:
Listing 2-43 shows the implementation of the
- Deletes weak references to the part's display frames.
First the method focuses on the display frames property of the part's storage unit, then removes and adds back the weak storage unit references associated with that property. This action deletes previously written persistent object references, which are not deleted by simply deleting the content of the value.
- Gets ID numbers for each display frame in the part's display frame list.
The method creates aCListIteratorobject to visit each of the part's display frames, retrieving the frame ID number for each.If, however, a draft key is passed in the
keyparameter, it indicates that the part is being cloned to another draft, in which case the method creates a weak clone of the display frame and uses the frame ID of the cloned frame instead. A draft key is a unique number that identifies a cloning operation on a draft; because cloning is a multistep process, the key is needed to preserve the integrity of each operation.
- Writes out weak references for each of the part's display frames.
Still within the iteration loop of theCListIterator, the method gets the weak reference to the storage unit of each of the part's display frames. Finally, using a macro namedStorageUnitSetValue, the method writes that value into the display frames property of the part's storage unit.The
StorageUnitSetValuemacro, defined in the file StorUtil.h, simplifies handling of theODByteArraystructure required by theSetValuemethod ofODStorageUnit, which the macro calls.
ExternalizeStateInfomethod.Listing 2-43
ExternalizeStateInfomethod
void SamplePart::ExternalizeStateInfo( Environment* ev, ODStorageUnit* storageUnit, ODDraftKey key, ODFrame* scopeFrame ) { SOM_Trace("SamplePart","ExternalizeStateInfo"); ODStorageUnitRefweakRef; ODID frameID; ODID scopeFrameID = ( scopeFrame ? scopeFrame->GetID(ev) : kODNULLID ); ODDraft* fromDraft = ODGetDraft(ev,fSelf); storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined, kODWeakStorageUnitRefs, 0, kODPosUndefined); storageUnit->Remove(ev); storageUnit->AddValue(ev, kODWeakStorageUnitRefs); CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { frameID = proxy->GetID(); if ( key ) frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID, scopeFrameID); storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef); TRY StorageUnitSetValue(storageUnit, ev, kODStorageUnitRefSize, (ODPtr)&weakRef); CATCH_ALL // Consume the exception ENDTRY } }The ExternalizeContent Method
TheSamplePartobject'sExternalizeContentmethod is empty, although any implementation would contain a statement to focus the part's storage unit on its contents property (kODPropContents). Every part must have a property of this type in which to store its content data. OpenDoc uses the contents property to match parts to their correct part editors. Finally, the method would also write the part's content data out to its storage unit in an appropriate manner. SamplePart has no intrinsic content soExternalizeContentdoes nothing.The CloneInto Method
OpenDoc calls theCloneIntomethod during data interchange operations, that is, when a part is copied to the Clipboard, to a drag-and-drop object, or to a link-source object. TheCloneIntomethod is inherited from theODPersistentObjectclass. Generally, a part should respond to theCloneIntomethod call by writing its own data to the specified destination storage unit and cloning any objects to which it has strong persistent references and which are within the scope of the frame passed in theinitiatingFrameparameter.
The
- Note
- The scope of a frame includes the content of frames embedded within it but excludes other content of the parts belonging to those embedded frames. Scope and other concepts of cloning are explained in the OpenDoc Programmer's Guide for the Mac OS.
![]()
SamplePartobject's implementation of theCloneIntomethod writes only its own data, state information, and content to the destination storage unit. Because SamplePart does not support embedding of other parts within itself, it has no need to clone any other objects.SamplePart does the actual work of externalizing its data in the internal methods
CheckAndAddProperties(Listing 2-41).Listing 2-44 shows the implementation of the
CloneIntomethod.
void SamplePart::CloneInto( Environment* ev, ODDraftKey key, ODStorageUnit* destinationSU, ODFrame* initiatingFrame ) { SOM_Trace("SamplePart","CloneInto"); if ( destinationSU->Exists(ev, kODPropContents, kSamplePartKind, 0) == kODFalse ) { this->CheckAndAddProperties(ev, destinationSU); this->ExternalizeStateInfo(ev, destinationSU, key, initiatingFrame); this->ExternalizeContent(ev, destinationSU, key, initiatingFrame); } }The InternalizeContent Method
TheSamplePartobject's internalInternalizeContentmethod does nothing, because SamplePart has no intrinsic content.Generally speaking, for parts having content, a method such as this would focus the part's storage unit on the
kODPropContentsproperty, then read the stored data values. A reference to the storage unit is passed by OpenDoc to the part'sInitPartFromStoragemethod (which in turn calls this method).The InternalizeStateInfo Method
TheSamplePartobject calls its own internalInternalizeStateInfomethod from itsInitPartFromStoragemethod when it reads the part in from its persistent storage unit. This method reads in state information--any information pertaining to the working of the part editor--rather than the content. Generally, state information enables a part to present the same setup or configuration to the user as it had when last written out to storage.The
InternalizeStateInfomethod reads from storage a list of weak references to its display frames, previously written out by theExternalizeStateInfomethod. The method validates each reference; if the reference is valid, the method adds it to its display frame list using lazy internalization. That is, the method uses a frame proxy object, adding the proxy pointer to its display frame list. The part reads in the actual display frame object only when it is actually needed.Listing 2-45 shows the implementation of the
InternalizeStateInfomethod.Listing 2-45
InternalizeStateInfomethod
void SamplePart::InternalizeStateInfo( Environment* ev, ODStorageUnit* storageUnit ) { SOM_Trace("SamplePart","InternalizeStateInfo"); ODStorageUnitRefweakRef; ODULong size; if ( storageUnit->Exists(ev, kODPropDisplayFrames, kODWeakStorageUnitRefs, 0) ) { storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined, kODWeakStorageUnitRefs, 0, kODPosUndefined); size = storageUnit->GetSize(ev); storageUnit->SetOffset(ev, 0); for ( ODULong offset = 0; offset < size; offset += kODStorageUnitRefSize ) { TRY StorageUnitGetValue(storageUnit, ev, kODStorageUnitRefSize, (ODPtr)&weakRef); if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) ) { ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef); CFrameProxy* proxy = new CFrameProxy; proxy->InitFrameProxy(frameID, ODGetDraft(ev,storageUnit)); fDisplayFrames->Add(proxy); } CATCH_ALL // Consume exception ENDTRY } } }The ReadPartInfo Method
Every part is displayed in at least one frame represented by an object of classODFrame. Frame objects have a part info field in which a part editor can store information describing how it should display its part's data in that frame. When you write your part to storage, OpenDoc calls your part'sWritePartInfomethod, and when you load your part into memory, OpenDoc calls itsReadPartInfomethod. Generally, a part should respond to theWritePartInfomethod call by writing enough information to persistent storage to be able to reconstruct each frame's part info field, and it should perform that reconstruction in itsReadPartInfoimplementation. TheWritePartInfomethod is described in the section following this one.The
SamplePartobject stores a pointer to an object of a C++ helper class namedCFrameInfoin its part info field.The
ReadPartInfomethod performs the following actions:
If the
- Instantiates a frame info object.
TheCFrameInfoconstructor initializes the object's internal data fields.
- Reads the frame info object into memory.
TheInitFromStoragemethod reads theCFrameInfoobject, containing the frame's status information, from its storage unit.
- Returns a pointer to the frame info object.
CFrameInfoobject'sInitFromStoragemethod fails, the method deletes the object and propagates the exception to the calling method.Listing 2-46 shows the implementation of the
SamplePartobject'sReadPartInfomethod, theCFrameInfoconstructor (defined inline), and theCFrameInfoobject'sInitFromStoragemethod.Listing 2-46
ReadPartInfo,CFrameInfoconstructor, andCFrameInfo::InitFromStoragemethods
ODInfoType SamplePart::ReadPartInfo( Environment* ev, ODFrame* frame, ODStorageUnitView* storageUnitView ) { SOM_Trace("SamplePart","ReadPartInfo"); CFrameInfo* frameInfo = new CFrameInfo; TRY frameInfo->InitFromStorage(ev, storageUnitView); CATCH_ALL ODDeleteObject(frameInfo); RERAISE; ENDTRY return (ODInfoType)frameInfo; } CFrameInfo::CFrameInfo() { fFrameActive = kODFalse; fFrameReactivate = kODFalse; fShouldDisposeWindow = kODFalse; fActiveFacet = kODNULL; fSourceFrame = kODNULL; fDependentFrame = kODNULL; fPartWindow = kODNULL; } void CFrameInfo::InitFromStorage(Environment* ev, ODStorageUnitView* storageUnitView) { ODStorageUnit* storageUnit = storageUnitView->GetStorageUnit(ev); if ( storageUnit->Exists(ev, kODNULL, kSamplePartInfo, 0) ) { TRY storageUnit->Focus(ev, kODNULL, kODPosSame, kSamplePartInfo, 0 , kODPosUndefined); ODStorageUnitRef weakRef = {0,0,0,0}; StorageUnitGetValue(storageUnit, ev, sizeof(ODStorageUnitRef), (ODPtr)&weakRef); if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) ) { ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef); CFrameProxy* proxy = new CFrameProxy; proxy->InitFrameProxy(frameID, ODGetDraft(ev,storageUnit)); fSourceFrame = proxy; } CATCH_ALL ODDeleteObject(fSourceFrame); fSourceFrame = kODNULL; ENDTRY TRY ODStorageUnitRef weakRef = {0,0,0,0}; StorageUnitGetValue(storageUnit, ev, sizeof(ODStorageUnitRef), (ODPtr)&weakRef); if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) ) { ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef); CFrameProxy* proxy = new CFrameProxy; proxy->InitFrameProxy(frameID, ODGetDraft(ev,storageUnit)); fDependentFrame = proxy; } CATCH_ALL ODDeleteObject(fDependentFrame); fDependentFrame = kODNULL; ENDTRY } }The WritePartInfo Method
OpenDoc calls a part'sWritePartInfomethod for each of its display frames whenever the document is saved.The
SamplePartobject's implementation of theWritePartInfomethod calls theCFrameInfoobject'sExternalizemethod, which first gets a reference to the storage unit of the storage-unit view object passed with the call toWritePartInfo. TheExternalizemethod then calls theCFrameInfoobject'sCleanseFrameInfoPropertymethod, which iterates through the value types in the storage unit and removes any that are not supported by SamplePart. Finally,Externalizecalls theCFrameInfoobject'sExternalizeFrameInfomethod to actually write out the frame's part info data.The
CFrameInfoobject'sExternalizeFrameInfomethod works much the same as theSamplePartobject'sExternalizeStateInfomethod. That is, the method removes, then adds back, the value containing weak references in the storage unit. Then, the method writes weak references to its source frame, if any, and its dependent frame, if any. In both cases, if a draft key exists, the method creates a weak clone of the display frame and writes out the weak reference to the storage unit. TheSamplePartobject'sExternalizeStateInfomethod is described in "The ExternalizeStateInfo Method".Listing 2-47 shows the implementation of the
SamplePartobject'sWritePartInfomethod, theCFrameInfoobject'sExternalizemethod, and theCFrameInfoobject'sExternalizeFrameInfomethod.Listing 2-47
WritePartInfo,CFrameInfo::Externalize, andCFrameInfo::ExternalizeFrameInfomethods
void SamplePart::WritePartInfo( Environment* ev, ODInfoType partInfo, ODStorageUnitView* storageUnitView ) { SOM_Trace("SamplePart","WritePartInfo"); ((CFrameInfo*) partInfo)->Externalize(ev, storageUnitView); } void CFrameInfo::Externalize(Environment* ev, ODStorageUnitView* storageUnitView) { ODStorageUnit* storageUnit = storageUnitView->GetStorageUnit(ev); this->CleanseFrameInfoProperty(ev, storageUnit); this->ExternalizeFrameInfo(ev, storageUnit, kODNULLKey, kODNULL); } void CFrameInfo::ExternalizeFrameInfo(Environment* ev, ODStorageUnit* storageUnit, ODDraftKey key, ODFrame* scopeFrame) { if ( storageUnit->Exists(ev, kODNULL, kSamplePartInfo, 0) ) { storageUnit->Focus(ev, kODNULL, kODPosSame, kSamplePartInfo, 0, kODPosUndefined); storageUnit->Remove(ev); } storageUnit->AddValue(ev, kSamplePartInfo); { ODStorageUnitRef weakRef = {0,0,0,0}; if ( fSourceFrame ) { ODID frameID = fSourceFrame->GetID(); ODID scopeFrameID = ( scopeFrame ? scopeFrame->GetID(ev) : kODNULLID ); ODDraft* fromDraft = fSourceFrame->GetDraft(); if ( key ) frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID, scopeFrameID); storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef); } StorageUnitSetValue(storageUnit, ev, sizeof(ODStorageUnitRef), (ODPtr)&weakRef); } { ODStorageUnitRef weakRef = {0,0,0,0}; if ( fDependentFrame ) { ODID frameID = fDependentFrame->GetID(); ODID scopeFrameID = ( scopeFrame ? scopeFrame->GetID(ev) : kODNULLID ); ODDraft* fromDraft = fDependentFrame->GetDraft(); if ( key ) frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID, scopeFrameID); storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef); } StorageUnitSetValue(storageUnit, ev, sizeof(ODStorageUnitRef), (ODPtr)&weakRef); } }The ClonePartInfo Method
OpenDoc calls a part'sClonePartInfomethod when any of its display frames is cloned during data transfer. Generally, a part editor should respond to theClonePartInfomethod call by writing out the frame's part info data, including any additional objects to which the part has strong persistent references and that are within the scope of the specified frame.The
SamplePartobject's implementation ofClonePartInfocalls theCloneIntomethod of theCFrameInfohelper object holding the specified frame's part info data. TheCFrameInfoimplementation ofCloneIntogets the storage unit, prefocused to a property but not to a value, and writes out the frame's part info data by calling theCFrameInfoobject'sExternalizeFrameInfomethod, which is shown in Listing 2-47.Listing 2-48 shows the implementation of the
SamplePartobject'sClonePartInfomethod and theCFrameInfoobject'sCloneIntomethod.Listing 2-48
ClonePartInfoandCFrameInfo::CloneIntomethods
void SamplePart::ClonePartInfo( Environment* ev, ODDraftKey key, ODInfoType partInfo, ODStorageUnitView* storageUnitView, ODFrame* scopeFrame ) { SOM_Trace("SamplePart","ClonePartInfo"); ((CFrameInfo*) partInfo)->CloneInto(ev, key, storageUnitView, scopeFrame); } void CFrameInfo::CloneInto(Environment *ev, ODDraftKey key, ODStorageUnitView* storageUnitView, ODFrame* scopeFrame) { ODStorageUnit* storageUnit = storageUnitView->GetStorageUnit(ev); if ( storageUnit->Exists(ev, kODNULL, kSamplePartInfo, 0) == kODFalse ) { this->ExternalizeFrameInfo(ev, storageUnit, key, scopeFrame); } }The Release Method
A part'sReleasemethod is called by an object, such as another part editor, whenever it releases a reference to this part. TheReleasemethod is inherited from theODRefCntObjectclass, and the inherited implementation does the actual reference-count management. Thesom_SamplePartobject'sReleasemethod calls the inherited method before it calls theSamplePartobject'sReleasemethod described in this section (see also "SamplePart System Object Model Interface").The
SamplePartobject's implementation of theReleasemethod releases the part-wrapper object, to which thefSelffield points, if its reference count falls to 0.Listing 2-49 shows the implementation of the
Releasemethod.
void SamplePart::Release( Environment* ev ) { SOM_Trace("SamplePart","Release"); if ( fSelf->GetRefCount(ev) == 0 ) ODGetDraft(ev,fSelf)->ReleasePart(ev,fSelf); }The ReleaseAll Method
OpenDoc calls a part'sReleaseAllmethod when the part object is about to be deleted by its draft. At this point, the part must release all the references it has acquired to other reference-counted objects. Otherwise, it will cause an invalid reference count error at some later time. This method is inherited from theODPersistentObjectclass. Thesom_SamplePartobject'sReleaseAllmethod calls the inherited method after it calls theSamplePartobject'sReleaseAllmethod described in this section (see also "SamplePart System Object Model Interface").The
SamplePartobject's implementation of theReleaseAllmethod performs the following actions:
The ODDeleteObject and
- Cleans up the SamplePart global variables.
TheReleaseAllmethod first ensures that the global variables are no longer needed. The global variables are shared among all instances of theSamplePartclass that are currently running, and each instance increments a usage count accordingly. The method decrements the usage count. If the usage count reaches 0, the method releases the menu bar object, deletes the user interface focus set object, and deletes the global variables structure.
- Cleans up the part's display frame list.
TheReleaseAllmethod first ensures that the part's display frame list is not null. SamplePart maintains proxy display frame objects in its list to support lazy internalization; the actual frames are not read into memory until they are needed. The method iterates through the display frame list, removing the pointer for each proxy from the list and deleting the proxy object. Then the method deletes the frame list object itself.
ODReleaseObjectutility macros, used in theReleaseAllmethod to delete objects and release reference-counted objects, are defined in the ODUtils.h file.Listing 2-50 shows the implementation of the
ReleaseAllmethod.Listing 2-50
ReleaseAllmethod
void SamplePart::ReleaseAll( Environment* ev ) { SOM_Trace("SamplePart","ReleaseAll"); TRY if ( --gGlobalsUsageCount == 0 ) { ODReleaseObject(ev, gGlobals->fMenuBar); ODDeleteObject(gGlobals->fUIFocusSet); ODDeleteObject(gGlobals); } if ( fDisplayFrames ) { CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { fiter.RemoveCurrent(); delete proxy; } ODDeleteObject(fDisplayFrames); } CATCH_ALL RERAISE; ENDTRY }The Purge Method
When OpenDoc detects a possible shortage of memory, it may call a part'sPurgemethod. The part should free as much memory as possible. OpenDoc passes in the requested number of bytes to free with the method call. Obviously, parts should not free any resources they need to keep running.The
SamplePartobject's implementation of thePurgemethod performs the following actions:
Listing 2-51 shows the implementation of the
- Checks the view type of each of its display frames.
The method checks first to see if its internal list of display frames has been created. If not, there is no storage to free, so the method returns. Otherwise, the method iterates through all the frame proxy objects in its display frames list. If the frame associated with the proxy has not been loaded into memory, the method ignores it.
- Releases the unused thumbnail resource.
The method ensures that no frame has a view type of thumbnail, as determined in its previous iteration of its frame list, and that the thumbnail resource was previously read into memory. If these conditions prevail, the method increments its count of freed bytes by the size of the resource, releases the resource, and sets to null its global variable that points to the resource.
- Returns the cumulative count of the number of bytes freed.
Purgemethod.
ODSize SamplePart::Purge( Environment* ev, ODSize /*size*/ ) { SOM_Trace("SamplePart","Purge"); if ( fDisplayFrames == kODNULL ) return 0; ODSize bytesFreed = 0; ODBooleanusingThumbnail= kODFalse; CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { if ( proxy->FrameIsLoaded() ) { ODTypeTokenframeView = proxy->GetFrame(ev)->GetViewType(ev); if ( frameView == gGlobals->fThumbnailView ) usingThumbnail = kODTrue; proxy->Purge(ev); } } if ( !usingThumbnail && (gGlobals->fThumbnail != kODNULL) ) { bytesFreed += (ODSize) ODGetHandleSize(gGlobals->fThumbnail); ReleaseResource(gGlobals->fThumbnail); gGlobals->fThumbnail = kODNULL; } return bytesFreed; }The SetDirty Method
TheSamplePartobject's internalSetDirtymethod sets its dirty flag to true, indicating that the part's data has been changed by the user. The part editor calls its ownSetDirtymethod whenever it changes its content.The
SetDirtymethod performs the following actions:
Listing 2-52 shows the implementation of the
- Checks the dirty flag and write status.
The implementation is protected by its own flag, the internal variablefDirty. If the flag is already true, or if the part's draft is read-only, the method body doesn't execute. Otherwise the method performs the subsequent steps.
- Sets the dirty flag to true.
- Notifies the draft that its content has changed from its previous version.
The method gets access to the draft through theODGetDraftutility method and calls itsSetChangedFromPrevmethod.
SetDirtymethod.
void SamplePart::SetDirty( Environment* ev ) { SOM_Trace("SamplePart","SetDirty"); if ( !fDirty && !fReadOnlyStorage ) { fDirty = kODTrue; ODGetDraft(ev,fSelf)->SetChangedFromPrev(ev); } }